<!DOCTYPE HTML>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="utf-8">
  
  <title>Spring-IoC揭秘 › 但行好事，莫问前程</title>
  <meta name="author" content="fengbo">
  
  <meta name="description" content="不想当架构师的程序员不是好厨师 ，不认为PHP是世界上最好的编程语言的Python开发者不是一个好的Javaer。">
  
  
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

  <meta property="og:title" content="Spring-IoC揭秘">
  <meta property="og:site_name" content="但行好事，莫问前程">

  
    <meta property="og:image" content="undefined">
  

  <link href="/favicon.png" rel="icon">
  <link rel="alternate" href="/atom.xml" title="但行好事，莫问前程" type="application/atom+xml">
  <link rel="stylesheet" href="/css/style.css" media="screen" type="text/css">
  <!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
  

</head>
</html>

<body>
  <header id="header"><div class="meta inner">
  <h1><a href="/">但行好事，莫问前程</a></h1>
  <h2><a href="/">CV工程师，了解面向Google编程的基本原理，有三年使用Google经验，在通往程序员的道路上艰难的爬行着。</a></h2>
  <nav id="main-nav">
    <ul>
      
      <li><a href="/">Home</a></li>
      
      <li><a href="/archives">Archives</a></li>
      
      <li><a href="/atom.xml">RSS</a></li>
      
    </ul>
    <div class="clearfix"></div>
  </nav>
</div>
<div class="clearfix"></div>
</header>
  <div id="content" class="inner">
    <div id="main-col" class="alignleft"><div id="wrapper"><article class="post">
  <div class="post-content">
    <header>
      
  
    <h1 class="title">Spring-IoC揭秘</h1>
  

      
        <time datetime="2018-06-16T07:22:30.000Z">2018-06-16</time>
      
    </header>
    <div class="entry">
      
        <h2 id="IoC原理解析"><a href="#IoC原理解析" class="headerlink" title="IoC原理解析"></a>IoC原理解析</h2><p>原来需要手动new对象，并且用完之后对象可能被废弃了，而Spring IoC可以帮助我们创建对象、管理对象的生命周期、对对象的依赖进行注入等。具体可以分为容器启动阶段和实例化阶段，容器启动阶段主要是解析配置文件，以XML为例，通过XmlBeanDefinitionReader加载所有的xml文件并在内部调用DefaultBeanDefinitionDocumentReader解析xml各个节点，主要是将bean标签解析到BeanDefinition中，在然后就可以通过BeanDefinitionRegistry将BeanDefinition添加到BeanDefinition容器中。实例化阶段在调用getBean方法后开始，一般我们在程序中调用getBean方法后会先去单例容器尝试获取该实例，如果存在就可以直接返回，如果不存在就会去判断该bean是否是单例的，然后去BeanDefinition容器获取该key对应的BeanDefinition对象，然后根据BeanDefinition去构建需要返回的对象实例。大概步骤是这样，中间还有很多需要注意的点，比如会尝试从父容器获取bean、对BeanPostProcessors的调用、循环依赖的问题等。</p>
<h3 id="循环依赖问题"><a href="#循环依赖问题" class="headerlink" title="循环依赖问题"></a>循环依赖问题</h3><p>Spring使用三级缓存来解决循环依赖问题，即将已经创建出来还没有注入依赖的对象放入singletonsCurrentlyInCreation缓存中，循环依赖时可以将还没有构建完成的实例从该容器中拿出来，并进行依赖注入。所以如果是构造方法注入的循环依赖，Spring会抛出异常。</p>
<h3 id="容器启动阶段"><a href="#容器启动阶段" class="headerlink" title="容器启动阶段"></a>容器启动阶段</h3><ol>
<li>Spring通过XmlBeanDefinitionReader解析XML配置将配置中的bean的基本信息解析到BeanDefinition实例中。</li>
<li>通过BeanDefinitionRegistry接口的registerBeanDefinition方法将BeanDefinition注册到BeanDefinition容器，即存放到变量名为beanDefinitionMap的Map集合中。</li>
<li>使用BeanFactoryPostProcessor在容器实例化相应对象之前，对注册到容器的BeanDefinition所保存的信息做相应的修改。可以以此来修改bean定义的某些属性，为bean定义增加其他信息等。<ul>
<li>PropertyPlaceholderConfigurer实现类：允许我们在XML配置文件中使用占位符（PlaceHolder），并将这些站位符所代表的资源单独配置到简单的properties文件中来加载（例如数据库的数据源等配置内容，一般写在配置文件中）。</li>
<li>PropertyOverrideConfigurer实现类：可以对容器中配置的任何你想处理的bean定义的property信息进行覆盖替换。即读取properties文件中的配置项，用读取到beanName.propertyName=value的value值覆盖掉对应的原来XML中bean定义的property信息。</li>
<li>CustomEditorConfigurer实现类：容器从XML中读取的都是字符串形式的，最终的应用程序却是由各种类型的对象构成。要想完成这种由字符串到具体对象的转换，就需要转换规则相关的信息，CustomEditorConfigurer类就是负责将我们自己实现的PropertyEditor接口的实现类告知容器的。<a id="more"></a>
</li>
</ul>
</li>
</ol>
<h3 id="Bean实例化阶段"><a href="#Bean实例化阶段" class="headerlink" title="Bean实例化阶段"></a>Bean实例化阶段</h3><p><img src="/img/Spring-bean生命周期.JPG"><br>实例化阶段又分为实例化和初始化，实例化是指创建一个bean的过程，即调用bean的构造函数；初始化指初始化bean属性，即调用bean的setter方法设置bean属性。</p>
<ol>
<li>当某个请求方通过容器的getBean方法明确地请求某个对象，或者因依赖关系容器需要隐式地调用getBean方法时，先检查所请求的对象之前是否已经初始化，即检查DefaultSingletonBeanRegistry类的变量名为singletonObjects的Map集合中是否有所需要的对象。</li>
<li>如果没有，则判断是否为单例，如果是，根据注册的BeanDefinition所提供的信息实例化被请求对象，为其注入依赖，并存放到singletonObjects容器中。</li>
<li>检查当前对象实例是否实现了一系列的以Aware命名结尾的回调接口。如果是，则将这些Aware接口定义中规定的依赖注入给当前对象实例。<ul>
<li>BeanNameAware：实现该接口的对象可以获取该对象实例对应的beanName。</li>
<li>BeanClassLoaderAware：实现该接口的对象可以获取加载该对象的Classloader。默认会使用加载org.springframework.util.ClassUtils类的Classloader。</li>
<li>BeanFactoryAware：实现该接口的对象可以获取到BeanFactory容器本身。</li>
</ul>
</li>
<li>使用BeanPostProcessor接口对所有符合条件的实例化后的对象实例进行处理。<ul>
<li>postProcessBeforeInitialization()方法是上图中BeanPostProcessor前置处理这一步将会执行的方法。</li>
<li>postProcessAfterInitialization()则是对应上图中BeanPostProcessor后置处理那一步将会执行的方法。</li>
</ul>
</li>
<li>InitializingBean接口、bean的init-method属性和@PostConstruct注解：在对象实例化过程调用过BeanPostProcessor的前置处理之后，会接着检测当前对象是否实现了InitializingBean接口，如果是，则会调用其afterPropertiesSet()方法进一步调整对象实例的状态。</li>
<li>DisposableBean接口、bean的destroy-method属性和@PreDestory注解：当所有的一切，该设置的设置，该注入的注入，该调用的调用完成之后，容器将检查singleton类型的bean实例，看其是否实现了org.springframework.beans.factory.DisposableBean接口。或者其对应的bean定义是否通过bean的destroy-method属性指定了自定义的对象销毁方法。如果是，就会为该实例注册一个用于对象销毁的回调（Callback），以便在这些singleton类型的对象实例销毁之前，执行销毁逻辑。</li>
</ol>
<p>ps：可以在 org.springframework.beans.factory.support.<b>AbstractBeanFactory</b>类的代码中查看到getBean()方法的完整实现逻辑，可以在其子类org.springframework.beans.factory.support.<b>AbstractAutowireCapableBeanFactory</b>的代码中一窥createBean()方法的全貌。</p>
<h2 id="统一资源加载策略"><a href="#统一资源加载策略" class="headerlink" title="统一资源加载策略"></a>统一资源加载策略</h2><h3 id="org-springframework-core-io-Resource"><a href="#org-springframework-core-io-Resource" class="headerlink" title="org.springframework.core.io.Resource"></a>org.springframework.core.io.Resource</h3><ul>
<li>ByteArrayResource：将字节（byte）数组提供的数据作为一种资源进行封装，如果通过InputStream形式访问该类型的资源，该实现会根据字节数组的数据，构造相应的ByteArrayInputStream并返回。</li>
<li>ClassPathResource ：该实现从Java应用程序的ClassPath中加载具体资源并进行封装，可以使用指定的类加载器（ClassLoader）或者给定的类进行资源加载。</li>
<li>FileSystemResource：对java.io.File类型的封装，所以，我们可以以文件或者URL的形式对该类型资源进行访问，只要能跟File打的交道，基本上跟FileSystemResource也可以。</li>
<li>UrlResource：通过java.net.URL进行的具体资源查找定位的实现类，内部委派URL进行具体的资源操作。</li>
<li>InputStreamResource：将给定的InputStream视为一种资源的Resource实现类，较为少用。<br>可能的情况下，以ByteArrayResource以及其他形式资源实现代之。</li>
</ul>
<h3 id="org-springframework-core-io-ResourceLoader"><a href="#org-springframework-core-io-ResourceLoader" class="headerlink" title="org.springframework.core.io.ResourceLoader"></a>org.springframework.core.io.ResourceLoader</h3><ul>
<li>DefaultResourceLoader：默认实现类。<ol>
<li>首先检查资源路径是否以classpath:前缀打头，如果是，则尝试构造ClassPathResource类型资源并返回。</li>
<li>尝试通过URL，根据资源路径来定位资源，如果没有抛出MalformedURLException，有则会构造UrlResource类型的资源并返回；</li>
<li>委派getResourceByPath(String)方法来定位，DefaultResourceLoader的getResourceByPath(String)方法默认实现逻辑是，构造ClassPathResource类型的资源并返回。</li>
</ol>
</li>
<li>FileSystemResourceLoader：继承自DefaultResourceLoader，但覆写了getResourceByPath(String)方法，使之从文件系统加载资源并以FileSystemResource类型返回。</li>
<li>ResourcePatternResolver： ResourceLoader的扩展，ResourceLoader每次只能根据资源路径返回确定的单个Resource实例，而ResourcePatternResolver则可以根据指定的资源路径匹配模式，每次返回多个Resource实例。<ul>
<li>PathMatchingResourcePatternResolver：在构造该实例的时候，可以指定一个ResourceLoader，如果不指定的话，则PathMatchingResourcePatternResolver内部会默认构造一个DefaultResourceLoader实例。然后将匹配后确定的资源路径委派给该ResourceLoader加载，所以如果不指定任何ResourceLoade 的话，PathMatchingResourcePatternResolver在加载资源的行为上会与DefaultResourceLoader基本相同，只存在返回的 Resource 数量上的差异。</li>
</ul>
</li>
</ul>
<h3 id="classpath和classpath"><a href="#classpath和classpath" class="headerlink" title="classpath和classpath*"></a>classpath和classpath*</h3><ul>
<li>classpath：指向编译过后的classes目录。只会到你当前项目的classes路径中查找文件。<ul>
<li>对于maven的所有项目，配置文件一般放在resources目录下，当编译之后会自动复制到classes目录下。</li>
<li>非maven的所有项目，一般放在src目录下，编译之后也会自动复制到classes目录下面。</li>
</ul>
</li>
<li>classpath*：不仅包含当前项目classes路径，还对当前项目包含的jar文件的classes路径进行查找。</li>
<li>在多个classpath中存在同名资源时，classpath只会加载第一个，而classpath*会都加载。</li>
<li>classpath<em>需要遍历所有的classpath，所以加载速度是很慢的，因此，在规划的时候，应该尽可能规划好资源文件所在的路径，尽量避免使用classpath</em>。</li>
</ul>
<h2 id="org-springframework-beans-factory-config-BeanPostProcessor"><a href="#org-springframework-beans-factory-config-BeanPostProcessor" class="headerlink" title="org.springframework.beans.factory.config.BeanPostProcessor"></a>org.springframework.beans.factory.config.BeanPostProcessor</h2><p>BeanPostProcessors在spring中是一个非常重要的扩展接口，它使得我们可以在创建bean实例的前后做一些自己的处理。</p>
<h3 id="方法描述"><a href="#方法描述" class="headerlink" title="方法描述"></a>方法描述</h3><ul>
<li>postProcessBeforeInitialization(Object bean, String beanName)：在初始化之前调用。具体在AbstractAutowireCapableBeanFactory类的applyBeanPostProcessorsBeforeInitialization方法中被循环调用。</li>
<li>postProcessAfterInitialization(Object bean, String beanName)：在初始化之后调用。具体在AbstractAutowireCapableBeanFactory类的applyBeanPostProcessorsAfterInitialization方法中被循环调用。</li>
<li>applyBeanPostProcessorsBeforeInitialization方法和applyBeanPostProcessorsAfterInitialization方法都是在本类的initializeBean(String, Object, RootBeanDefinition)方法中被调用。initializeBean则被本类的doCreateBean方法调用。</li>
</ul>
<h2 id="BeanPostProcessor子接口"><a href="#BeanPostProcessor子接口" class="headerlink" title="BeanPostProcessor子接口"></a>BeanPostProcessor子接口</h2><h3 id="org-springframework-beans-factory-config-InstantiationAwareBeanPostProcessor"><a href="#org-springframework-beans-factory-config-InstantiationAwareBeanPostProcessor" class="headerlink" title="org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor"></a>org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor</h3><p>BeanPostProcessor作用于初始化阶段，而InstantiationAwareBeanPostProcessor作用于实例化阶段。</p>
<ul>
<li>postProcessBeforeInstantiation(Class beanClass, String beanName)：实例化之前调用。具体在AbstractAutowireCapableBeanFactory类的applyBeanPostProcessorsBeforeInstantiation方法中被循环调用。</li>
<li>postProcessAfterInstantiation(Class beanClass, String beanName)：实例化之后设置bean属性时调用。具体在AbstractAutowireCapableBeanFactory类的populateBean方法中被循环调用。</li>
</ul>
<h3 id="org-springframework-beans-factory-config-SmartInstantiationAwareBeanPostProcessor"><a href="#org-springframework-beans-factory-config-SmartInstantiationAwareBeanPostProcessor" class="headerlink" title="org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor"></a>org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor</h3><p>该接口继承了InstantiationAwareBeanPostProcessor接口。</p>
<ul>
<li>predictBeanType(Class beanClass, String beanName)：预测bean的类型，返回第一个预测成功的Class类型，如果不能预测返回null。具体在类AbstractAutowireCapableBeanFactory的predictBeanType(String beanNama, RootBeanDefinition mbd, Class[] typesToMatch)方法中循环调用。</li>
<li>determineCandidateConstructors(Class beanClass, String beanName)：选择合适的构造器，比如目标对象有多个构造器，在这里可以进一步定制化，选择合适的构造器。具体在类AbstractAutowireCapableBeanFactory的determineConstructorsFromBeanPostProcessors(Class beanClass, String beanName)方法中循环调用。</li>
<li>getEarlyBeanReference(Object bean, String beanName)：获得提前暴露的bean引用，主要用来解决循环引用问题，只有单例对象才会调用此方法。具体在类AbstractAutowireCapableBeanFactory的getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean)方法中循环调用。</li>
</ul>
<h2 id="org-springframework-context-ApplicationContext"><a href="#org-springframework-context-ApplicationContext" class="headerlink" title="org.springframework.context.ApplicationContext"></a>org.springframework.context.ApplicationContext</h2><p><b>ApplicationContext是在BeanFactory的基础上构建</b>，不但拥有BeanFactory所有功能，同时提供了统一资源加载策略、国际化信息支持、容器类别事件发布等特性。而且ApplicationContext所管理的对象，在该容器启动之后，<b>默认全部初始化并绑定完成。</b>而不是像BeanFactory当客户端对象需要访问容器中的某个受管对象时，才对该对象进行初始化及依赖注入操作。即ApplicationContext会在启动阶段的活动完成后，紧接着通过<b>org.springframework.context.support.AbstractApplicationContext的refresh()方法</b>调用注册到该容器所有bean定义的实例化方法getBean()隐式初始化所有的bean。<br><img src="/img/ApplicationContext.png"><br>Img1.3.对BeanFactoryPostProcessor的实例类进行预处理。<br>Img1.4.在此处通过PostProcessorRegistrationDelegate对BeanFactoryPostProcessor进行初始化并执行了postProcessBeanFactory方法。<br>Img1.5.在此处通过PostProcessorRegistrationDelegate对所有的BeanPostProcessor实例初始化并注册到容器。<br>Img1.6.如果有相关bean，则负责配置国际化操作。<br>Img1.7.指定SimpleApplicationEventMulticaster作为系统观察者。<br>Img1.9.在此处为观察者添加被观察者。<br>Img1.10.对所有剩余普通的Bean(non-lazy-init)的初始化并注册到容器中。</p>

      
    </div>
      
      <footer>
        
        
  
  <div class="tags">
    <a href="/tags/Spring基础/">Spring基础</a>
  </div>

        
  <div class="addthis addthis_toolbox addthis_default_style">
    
      <a class="addthis_button_facebook_like" fb:like:layout="button_count"></a>
    
    
      <a class="addthis_button_tweet"></a>
    
    
      <a class="addthis_button_google_plusone" g:plusone:size="medium"></a>
    
    
      <a class="addthis_button_pinterest_pinit" pi:pinit:layout="horizontal"></a>
    
    <a class="addthis_counter addthis_pill_style"></a>
  </div>
  <script type="text/javascript" src="//s7.addthis.com/js/300/addthis_widget.js"></script>

      
      <div class="clearfix"></div>
      </footer>
  </div>
</article>

<!-- 来必力City版安装代码 -->
<!-- <div id="lv-container" data-id="city" data-uid="MTAyMC8yOTQ5MS82MDU5">
<script type="text/javascript">
   (function(d, s) {
       var j, e = d.getElementsByTagName(s)[0];

       if (typeof LivereTower === 'function') { return; }

       j = d.createElement(s);
       j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
       j.async = true;

       e.parentNode.insertBefore(j, e);
   })(document, 'script');
</script>
<noscript>为正常使用来必力评论功能请激活JavaScript</noscript>
</div> -->
<!-- City版安装代码已完成 -->

</div></div>
    <aside id="sidebar" class="alignright">
  
<div class="widget tagcloud">
  <h3 class="title">文章分类</h3>
  <div class="entry">
    <a href="/tags/JavaWeb/" style="font-size: 17.14px;">JavaWeb</a> <a href="/tags/Java基础/" style="font-size: 15.71px;">Java基础</a> <a href="/tags/Java高级特性/" style="font-size: 15.71px;">Java高级特性</a> <a href="/tags/Python/" style="font-size: 10px;">Python</a> <a href="/tags/Spring基础/" style="font-size: 14.29px;">Spring基础</a> <a href="/tags/WebRTC/" style="font-size: 10px;">WebRTC</a> <a href="/tags/其它/" style="font-size: 20px;">其它</a> <a href="/tags/分布式/" style="font-size: 11.43px;">分布式</a> <a href="/tags/硬件/" style="font-size: 12.86px;">硬件</a> <a href="/tags/计算机基础/" style="font-size: 18.57px;">计算机基础</a> <a href="/tags/设计模式/" style="font-size: 17.14px;">设计模式</a>
  </div>
</div>



    <div class="widget tagcloud">
    <h3 class="title">电子书</h3>
    <ul class="entry">
      <li class='link'><a href='http://www.duokan.com/' target="_blank">多看阅读</a>&nbsp;&nbsp;<a href='https://read.douban.com/' target="_blank">豆瓣阅读</a>&nbsp;&nbsp;<a href='http://e.dangdang.com/' target="_blank">当当阅读</a></li>
      <li class='link'><a href='http://www.ituring.com.cn/' target="_blank">图灵社区</a>&nbsp;&nbsp;<a href='https://www.epubit.com/' target="_blank">异步社区</a>&nbsp;&nbsp;<a href='https://www.geekbang.org/' target="_blank">极客空间</a></li>
    </ul>
  </div>


  
  <div class="widget tag">
    <h3 class="title">友情链接</h3>
      <ul class="entry">
        
          <li class='link'><a href='http://www.cnblogs.com/jietang/' target="_blank">唐洁的博客</a></li>
        
          <li class='link'><a href='http://rednaxelafx.iteye.com/' target="_blank">R大的博客</a></li>
        
          <li class='link'><a href='http://www.hollischuang.com/' target="_blank">阿里大神的博客</a></li>
        
          <li class='link'><a href='http://blog.csdn.net/IT_faquir/' target="_blank">IT_faquir的专栏</a></li>
        
      </ul>
  </div>


  
  <div class="widget tag">
    <h3 class="title">思维导图</h3>
      <ul class="entry">
        
          <li class='link'><a href='https://fengbo4213.github.io/img/开源协议.png' target="_blank">开源协议简介</a></li>
        
          <li class='link'><a href='https://www.processon.com/diagraming/5b0cf757e4b009aef58d4b9d' target="_blank">我的知识体系</a></li>
        
          <li class='link'><a href='https://www.processon.com/view/link/5a2a00e5e4b015e677290b4f' target="_blank">简单的网站架构</a></li>
        
      </ul>
  </div>


  
  <div class="widget tag">
    <h3 class="title">在线手册</h3>
      <ul class="entry">
        
          <li class='link'><a href='http://v3.bootcss.com/' target="_blank">BootStrap官方手册</a></li>
        
          <li class='link'><a href='http://www.iconfont.cn/http://element.eleme.io/#/zh-CN/' target="_blank">阿里巴巴矢量图标库</a></li>
        
      </ul>
  </div>


  
<div class="widget tag">
  <h3 class="title">最新文章</h3>
  <ul class="entry">
    
      <li>
        <a href="/2123/08/30/基础之MySQL原理/">MySQL原理</a>
      </li>
    
      <li>
        <a href="/2024/01/01/其它之ClickHouse索引/">ClickHouse索引</a>
      </li>
    
      <li>
        <a href="/2023/12/17/其它之MySQL线程池/">MySQL线程池</a>
      </li>
    
      <li>
        <a href="/2023/12/16/其它之InnoDB性能优化基础/">InnoDB性能优化基础</a>
      </li>
    
      <li>
        <a href="/2023/12/03/其它之Java8之后的版本新特性/">Java8之后的版本新特性</a>
      </li>
    
  </ul>
</div>

</aside>
    <div class="clearfix"></div>
  </div>
  <footer id="footer" class="inner"><div class="alignleft">
  
  &copy; 2024 fengbo
  
</div>
<div class="clearfix"></div></footer>
  <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>



</body>
</html>

